/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "playback.h"
#include <string.h>
#include "debug.h"
#include <Ecore.h>
#include <unistd.h>

static Collections *collections = NULL;
static struct list_head *playlist = NULL;
static struct list_head *now_playing;
static void handle_gst_message(GstMessage *message);
static int poll_gst(void *data);
static void _mkk_send_event(int type, unsigned long value, const char *message);

static int SHUFFLE = FALSE;
static int REPEAT = FALSE;

gint
max(gint a, gint b)
{
   return (a > b) ? a : b;
}

gint
min(gint a, gint b)
{                       
   return (a > b) ? b : a;
}

static GstElement *
_pipeline_instance()
{
   static GstElement *pipeline = NULL;
   if(!pipeline)
     pipeline = gst_element_factory_make ("playbin", "player");
   return pipeline;
}

static GstBus *
_bus_instance()
{
   static GstBus *bus = NULL;
   if(!bus)
     bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline_instance()));
   return bus;
}

int
mokoko_media_init(int argc, char ** argv)
{
   GError *error;
   Ecore_Timer *poll_gst_timer;
   if(!gst_init_check(&argc, &argv, &error))
     return FALSE;

   //  Init the event id.
   MKK_PLAYBACK_EVENT = ecore_event_type_new();
   
   poll_gst_timer = ecore_timer_add(1.0, poll_gst, NULL);

   return TRUE;
}

void
mokoko_media_deinit()
{
   gst_object_unref(_bus_instance());
   gst_deinit();
}

static void
handle_gst_message(GstMessage *message)
{
   switch (GST_MESSAGE_TYPE (message)) 
     {
        case GST_MESSAGE_ERROR: {
          GError *err;
          gchar *debug;
          gst_message_parse_error (message, &err, &debug);
          debug("Error code: %d, error message:  %s\n", err->code, err->message);
          _mkk_send_event(MKK_EVENT_TYPE_ERROR, err->code, err->message);
          g_error_free (err);
          g_free (debug);
          break;
        }
        case GST_MESSAGE_EOS:
          debug("message EOS\n");
          mokoko_media_play_next();
          break;
        case GST_MESSAGE_STATE_CHANGED: {
          GstState oldState;
          GstState newState;
          GstState pending;
          gst_message_parse_state_changed(message, &oldState, &newState, &pending);
          switch (newState)
            {
               case GST_STATE_VOID_PENDING:
               case GST_STATE_NULL:
               case GST_STATE_READY:
               case GST_STATE_PAUSED:
               case GST_STATE_PLAYING:
                 break;
            }
          break;
        }
        default:
          break;
     }
}

static int
poll_gst(void *data)
{
   GstMessage *message;
   while( (message = gst_bus_poll(_bus_instance(), GST_MESSAGE_ANY, 0)) != 0)
     {
        handle_gst_message(message);
     }
   return TRUE;
}

static void
_mkk_send_event(int type, unsigned long value, const char *message)
{
   MKK_Event *ev; 
   ev = calloc(1, sizeof(MKK_Event));
   ev->type = type;
   ev->value = value;
   if(message)
     ev->message = strdup(message);

   ecore_event_add(MKK_PLAYBACK_EVENT, ev, NULL, NULL);
}

int
mokoko_media_play(const char *file)
{
   char song_file_path[PATH_MAX];
   unsigned long length;
   gboolean b;

   if(!file) return FALSE;

   if(strstr(file, "file://") == file)
     snprintf(song_file_path, sizeof(song_file_path),
              "%s", file);
   else
     snprintf(song_file_path, sizeof(song_file_path),
              "file://%s", file);

   g_object_set (G_OBJECT (_pipeline_instance()), "uri", song_file_path, NULL);
   b = gst_element_set_state (GST_ELEMENT (_pipeline_instance()), GST_STATE_PLAYING);
   _mkk_send_event(MKK_EVENT_TYPE_STATUS, MOKOKO_STATE_PLAYING, NULL);
   length = mokoko_media_track_length_get();
   debug("playing %s .....Volume = %d Length=%d:%.2ds\n", song_file_path,
                          mokoko_media_volume_get(),
                          (int)(length / 60000), (int)((length/1000) % 60) );
   return b;
}

void
mokoko_media_pause()
{
   gst_element_set_state (GST_ELEMENT (_pipeline_instance()), GST_STATE_PAUSED);
   _mkk_send_event(MKK_EVENT_TYPE_STATUS, MOKOKO_STATE_PAUSED, NULL);
}

void
mokoko_media_resume()
{
   gst_element_set_state (GST_ELEMENT (_pipeline_instance()), GST_STATE_PLAYING);
   _mkk_send_event(MKK_EVENT_TYPE_STATUS, MOKOKO_STATE_PLAYING, NULL);
}

void
mokoko_media_stop()
{
   mokoko_media_pause();
   gst_element_set_state (GST_ELEMENT (_pipeline_instance()), GST_STATE_NULL);
   _mkk_send_event(MKK_EVENT_TYPE_STATUS, MOKOKO_STATE_NULL, NULL);
}

void
mokoko_media_play_next()
{
   Song *next;
   if(!now_playing || !now_playing->next)
     return;
   if(now_playing->next == playlist)
     {
        if(REPEAT)
          now_playing = playlist; 
        else
          return;
     }

   if(SHUFFLE)
     next = list_entry(now_playing->next, Song, shuffle_list_node);
   else
     next = list_entry(now_playing->next, Song, all_song_node);
   if(!next) return;
   now_playing = now_playing->next;
   mokoko_media_stop();
   mokoko_media_play(next->file);
}

void
mokoko_media_play_prev()
{
   Song *prev;
   if(!now_playing || !now_playing->prev)
     return;
   if(now_playing->prev == playlist)
     {
        if(REPEAT)
          now_playing = playlist; 
        else
          return;
     }

   if(SHUFFLE)
     prev = list_entry(now_playing->prev, Song, shuffle_list_node);
   else
     prev = list_entry(now_playing->prev, Song, all_song_node);
   if(!prev) return;
   now_playing = now_playing->prev;
   mokoko_media_stop();
   mokoko_media_play(prev->file);
}

Song *
mokoko_media_playing_song_get()
{
   Song *now_playing_song;
   if(!playlist) return NULL;
   if(now_playing == playlist) 
     now_playing = playlist->next;
   if(SHUFFLE)
     now_playing_song = list_entry(now_playing, Song, shuffle_list_node);
   else
     now_playing_song = list_entry(now_playing, Song, all_song_node);
   return now_playing_song;
}

unsigned long
mokoko_media_track_length_get()
{
   GstFormat format = GST_FORMAT_TIME;
   gint64 length = 0;

   gst_element_query_duration(_pipeline_instance(), &format, &length);
   return (length > 0) ? (length/1000000) : 0;
}

unsigned long
mokoko_media_track_position_get()
{
   GstFormat format = GST_FORMAT_TIME;
   signed long long position = 0;
   return (gst_element_query_position(_pipeline_instance(), &format, &position)) ? (position/1000000) : 0;
}

void
mokoko_media_track_position_set(unsigned long position)
{
   GstState pipe_state;
   gint64 pos;
   gboolean b;

   if (position < 0) position = 0;
   pipe_state = GST_STATE(_pipeline_instance());

   /*
   if ( (pipe_state != GST_STATE_PAUSED) && (pipe_state != GST_STATE_PLAYING) )
     return;
   */
   pos = position;
   pos = pos * 1000000;   // time in nano seconds.
   b = gst_element_seek(GST_ELEMENT(_pipeline_instance()), 1.0,
                GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                GST_SEEK_TYPE_SET, pos,
                GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
   if(!b)
     debug("Seek fail\n");
   else
     {
        _mkk_send_event(MKK_EVENT_TYPE_POSITION, position, NULL);
        debug("Setting track position to %d:%.2ds\n", (int)(position / 60000), (int)((position/1000) % 60) );
     }
}

void
mokoko_media_volume_up()
{
   mokoko_media_volume_set(min(100, mokoko_media_volume_get()+10));
}

void
mokoko_media_volume_down()
{
   mokoko_media_volume_set(max(0, mokoko_media_volume_get()-10));
}

void
mokoko_media_volume_set(guint volume)
{
   if (volume > 100) volume = 100;
   // Set playbin volume which ranges from 0.0 to 1.0
   g_object_set(G_OBJECT(_pipeline_instance()), "volume", volume/100.0, NULL);
   _mkk_send_event(MKK_EVENT_TYPE_VOLUME, volume, NULL);
   debug("Volume changed to %d\n", volume);
}

int
mokoko_media_volume_get()
{
   gdouble volume;
   g_object_get(G_OBJECT(_pipeline_instance()), "volume", &volume, NULL);
   return volume*100;
}

static int vol;
#define MUTE 0
void
mokoko_media_setMuted(int muted)
{
    if(muted) {
        vol = mokoko_media_volume_get();
        mokoko_media_volume_set(MUTE);
    }
    else {
        mokoko_media_volume_set(vol);
    }
}

int
mokoko_media_isMuted()
{
    if(mokoko_media_volume_get())
        return FALSE;
    else
        return TRUE;
}

void
mokoko_media_playlist_play(int repeat, int shuffle, int index)
{
   Song *now_playing_song = NULL;

   if (!collections) return;

   SHUFFLE = shuffle;
   REPEAT = repeat;

   if(SHUFFLE)
     {
        playlist = (&collections->shuffle_list_head);
        now_playing = playlist->next;
        while(index) 
          {
             now_playing = now_playing->next;
             index--;
          }
        now_playing_song = list_entry(now_playing, Song, shuffle_list_node);
     }
   else
     {
        playlist = (&collections->all_songs_head);
        now_playing = playlist->next;
        while(index) 
          {
             now_playing = now_playing->next;
             index--;
          }
        now_playing_song = list_entry(now_playing, Song, all_song_node);
     }

   if (!now_playing_song) 
       return;

   mokoko_media_stop();
   mokoko_media_play(now_playing_song->file);
}

void
mokoko_media_playlist_shuffle_set(int onoff)
{
   Song *now_playing_song;
   if(SHUFFLE == onoff) 
     return;
   SHUFFLE = onoff;
   if(!collections) return;
   // Switch now_playing and playlist to another list. 
   if(SHUFFLE)
     {
        shuffle_list_shuffle(collections);  // Do shuffle again.
        playlist = (&collections->shuffle_list_head);
        now_playing_song = list_entry(now_playing, Song, all_song_node);
        now_playing = &now_playing_song->shuffle_list_node;
     }
   else
     {
        playlist = (&collections->all_songs_head);
        now_playing_song = list_entry(now_playing, Song, shuffle_list_node);
        now_playing = &now_playing_song->all_song_node;
     }
}

int
mokoko_media_playlist_shuffle_get()
{
   return SHUFFLE;
}

void
mokoko_media_playlist_repeat_set(int onoff)
{
   REPEAT = onoff;
}

int
mokoko_media_playlist_repeat_get()
{
   return REPEAT;
}

MokokoState
mokoko_media_state_get() {
    return (MokokoState)_pipeline_instance()->current_state;
}

int
mokoko_media_collections_set(Collections *c)
{
   if(!c)
     return FALSE;
   collections = c; 
   shuffle_list_shuffle(collections);
   return TRUE;
}

int 
test (int argc, char ** argv) 
{
   if(!mokoko_media_init(argc, argv)) return FALSE;
   return TRUE;
}

